docs: explain the swr cache option on GET server actions - #1149
Conversation
The swr option of export const cache only ever appeared inside the
inline comment { maxAge, swr, public }, with no surface saying it
emits stale-while-revalidate=<n> (seconds) in Cache-Control, or when
to use it. Add the explanation next to every existing cache example
(docs site server-actions + data-fetching pages, AGENTS.md, the skill
reference) and name cached GET actions in the README feature list,
which previously only credited pages with Cache-Control.
Source of truth: cacheControlFor() in
packages/server/src/action-config.js.
Keep 'default private' adjacent to the option list in the AGENTS.md parenthetical, replace an ambiguous 'for up to that long' on the docs page, and drop a parenthetical that pulled the README sentence away from its own point (that these subsystems share one pluggable store).
The swr sentences had landed between the GET description and the mutation one, splitting the contrast the paragraph is built around. They belong beside the other cache sentences at the end.
vivek7405
left a comment
There was a problem hiding this comment.
I went over this against cacheControlFor() in packages/server/src/action-config.js and the GET response path in actions.js, and the facts hold: the number shorthand really does normalize to { maxAge: 60, swr: 0, public: false }, the scope is private unless public: true, stale-while-revalidate is appended only when swr > 0, and the weak ETag is attached to every GET-action response, so the README line about reads carrying Cache-Control plus an ETag is accurate.
What I did change was the prose. My first pass buried default private behind a long inserted clause in the AGENTS.md parenthetical, used an ambiguous "for up to that long" on the docs page, and dropped the swr sentences into the middle of the GET-versus-mutation contrast on data-fetching, which is the one structure that paragraph is built around. The README parenthetical was the worst of them: it pulled the sentence away from its own point, which is that these subsystems share one pluggable store, and the ETag has nothing to do with that. All four are fixed.
Both edited pages boot and render at 200 with the new prose in the output, and webjs check passes on website/. No behaviour change, so no test layer applies.
| WebSocket broadcast all share one pluggable store, so a single `setStore()` | ||
| call moves them onto Redis with no config files in between. | ||
| layer in the box. `cache()` for queries, HTTP Cache-Control for pages and for | ||
| cached GET server actions, a Session class with SessionStorage, NextAuth-style |
There was a problem hiding this comment.
This sentence is about everything sharing one pluggable store. An ETag parenthetical here is off that point, and the extra and made the list hard to parse.
| export const invalidates = (id) => ['user:' + id]; | ||
| export async function updateUser(id, data) { /* ... */ }</pre> | ||
| <p>The call site never changes (<code>await getUser(7)</code>). A <strong>GET</strong> rides its args in the URL, is CSRF-exempt, and is served with <code>Cache-Control</code> + an ETag, so a repeat read within the window comes from the browser cache and a stale one revalidates with a 304. A <strong>mutation</strong> sends a body, is CSRF-protected, and on completion its <code>invalidates</code> tags evict the matching server cache and tell the client to refetch the affected reads. A wrong request method is a <code>405</code>. It is additive: an action with no <code>method</code> stays a POST, exactly as before. The cache defaults to <code>private</code>; <code>{ public: true }</code> shares the response across users keyed only by URL, so use it only for data identical for every visitor, never a per-user read.</p> | ||
| <p>The call site never changes (<code>await getUser(7)</code>). A <strong>GET</strong> rides its args in the URL, is CSRF-exempt, and is served with <code>Cache-Control</code> + an ETag, so a repeat read within the window comes from the browser cache and a stale one revalidates with a 304. A <strong>mutation</strong> sends a body, is CSRF-protected, and on completion its <code>invalidates</code> tags evict the matching server cache and tell the client to refetch the affected reads. A wrong request method is a <code>405</code>. It is additive: an action with no <code>method</code> stays a POST, exactly as before. The cache defaults to <code>private</code>; <code>{ public: true }</code> shares the response across users keyed only by URL, so use it only for data identical for every visitor, never a per-user read. In the object form <code>maxAge</code> is the freshness window in seconds and <code>swr</code> adds a stale-while-revalidate grace window (also seconds), during which an expired response is still served instantly while the browser refreshes it in the background. The full header reference lives on the <a href="/docs/server-actions">server actions</a> page.</p> |
There was a problem hiding this comment.
The cache detail belongs down here with the other cache sentences. Sitting between the GET and mutation descriptions, it split the contrast the paragraph is built on.
Closes #1148
Documents the
swroption ofexport const cacheon GET server actions, which until now appeared only inside the inline comment{ maxAge, swr, public }with no prose on any surface explaining what it does. Also names cached GET server actions in the README feature list, which previously credited only pages with HTTP Cache-Control.What changed
website/app/docs/server-actions/page.ts: a new paragraph after the cached-GET example mapping thecacheexport onto the emittedCache-Controlheader (thecache = 60shorthand,maxAge,swras a stale-while-revalidate grace window in seconds,public, a concrete emitted header example, and when to reach forswr).website/app/docs/data-fetching/page.ts: one sentence onmaxAge+swrin the verb-actions section, linking to the server-actions page for the full header reference.AGENTS.md: a shortswrclause inside theexport const cacheparenthetical of the feat: HTTP-verb server actions via config exports (GET/POST/PUT/PATCH/DELETE) [epic] #488 paragraph..agents/skills/webjs/references/data-and-actions.md: a new bullet with the full header mapping (this file is also whatwebjs createcopies into every scaffolded app, so the scaffold surface syncs automatically, seepackages/cli/lib/create.jsL643).README.md: the feature list now reads "HTTP Cache-Control for pages and cached GET server actions (each read carries Cache-Control plus an ETag)".The prose matches
cacheControlFor()inpackages/server/src/action-config.js(L143): scopeprivateunlesspublic: true,max-age=<maxAge>, andstale-while-revalidate=<swr>appended only whenswr > 0, GET only.Surfaces checklist
llms.txtregenerates live, no manual edit.swrmention to sync).webjs checkpasses onwebsite/.Test plan
webjs checkpasses onwebsite/git grep -i 'stale.while.revalidate'now hits prose on both doc pages (previously only header examples elsewhere)